package com.service.atf.base.core;


import com.service.atf.base.exception.TFException;
import com.service.atf.base.model.BaseHeader;
import com.service.atf.base.type.BuffReaderHead;
import com.service.atf.base.type.BuffWriterHead;
import com.service.atf.base.type.ResultReplyRead;
import com.service.atf.base.utils.ATFConstants;
import com.service.atf.base.utils.ATFLogger;
import com.luotao.devp.base.db.Pager;
import com.luotao.devp.base.utils.ByteUtil;

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.util.concurrent.locks.ReentrantLock;


/**
 * =============================================================================<br>
 * 类型名称: XFilePoolWriter<br>
 * 类型版本: V1.0<br>
 * 类型描述: 读取TF文件的内容 <br>
 * 类型作者: luotao <br>
 * 创建日期: 2012-03-03 <br>
 * 类型备注: <br>
 * =============================================================================<br>
 * 修改日期 修改前版本 修改后版本 修改人 备注<br>
 * =============================================================================
 */
@SuppressWarnings("unused")
public class TFReader implements ITFInfo {

    private static final String TYPE_END = "23";

    private BuffReaderHead buffAdderHead;

    private BuffWriterHead buffReadAndWriterHead;

    private MappedByteBuffer buffBody;

    private String currentPath;

    private ReentrantLock wtlock = new ReentrantLock();

    //操作文件句柄
    private FileChannel channel;
    // 交换文件文件访问句柄
    private RandomAccessFile accfile;

    private boolean recover = true;

    public void setRecover(boolean recover) {
        this.recover = recover;
    }

    private Pager pager = new Pager();

    public Pager getPager() {
        return pager;
    }

    public String getCurrentPath() {
        return currentPath;
    }

    public boolean isRecover() {
        return recover;
    }


    private IReadProcess iReadProcess;

    public void setiReadProcess(IReadProcess iReadProcess) {
        this.iReadProcess = iReadProcess;
    }

    public TFReader(String filePath, int readIndex, int writeIndex, int pageSize, int totalCount, IReadProcess iReadProcess) {
        try {
            Pager pager = new Pager(pageSize);
            pager.setTotalCount(totalCount);
            currentPath = filePath;
            //设置分页信息
            this.pager = pager;
            this.iReadProcess = iReadProcess;
            accfile = new RandomAccessFile(filePath, "rw");
            channel = accfile.getChannel();
            initHead(readIndex, writeIndex);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public TFReader(String filePath, int readIndex, int writeIndex, int pageSize, int totalCount) {
        this(filePath, readIndex, writeIndex, pageSize, totalCount, null);
    }


    //根据指定的未知初始化内存
    public void initWithOffset(int position, int offset) {
        int positionAfterCheckpoint = position + offset;
        //计算checkpoint之后的记录的开始下标在文件的第几页
        int pageNoAfterCheckpoint = positionAfterCheckpoint / pager.getPageSize() + 1;
        pager.setPageNo(pageNoAfterCheckpoint);
        int leftSize = positionAfterCheckpoint - (pager.getFirst() - 1);
        createBody(pager.getFirst(), pager.getPageSize());
        buffReadAndWriterHead.write(positionAfterCheckpoint);
        buffBody.position(leftSize);
    }

    private void initHead(int readIndex, int writeIndex) {
        //ADDER指针
        buffAdderHead = new BuffReaderHead(channel, readIndex);
        //读取指针
        buffReadAndWriterHead = new BuffWriterHead(channel, writeIndex);
    }

    private boolean isOffsetEquals() {
        //获得读指针的偏移量
        int offsetRead = buffAdderHead.read();
        //获得写指针的偏移量
        int offsetWrite = buffReadAndWriterHead.read();
        return offsetRead > offsetWrite;
    }

    public int getWriterOffset() {
        //获得读指针的偏移量
        return buffReadAndWriterHead.read();
    }

    public int getAdderOffset() {
        return buffAdderHead.read();
    }

    public void updateWriterOffset(int value) {
        buffReadAndWriterHead.write(value);
    }

    /**
     * @return 返回0表示已经追赶到写指针，返回1表示业务已经处理完成
     */
    public ResultReplyRead readData() {
        try {
            wtlock.lock();

            //获得读指针的偏移量
            int adderOffset = buffAdderHead.read();
            int offsetRead = buffReadAndWriterHead.read();
//    ATFLogger.sysdebug("TFReader文件路径 : "+currentPath+" ，ADDER指针的偏移量："+adderOffset+",追赶指针的偏移量："+offsetRead);
            if (adderOffset == GlobalDefine.TF_DEFAULT_HEADER_VALUE) {
                return new ResultReplyRead(GlobalDefine.TF_STATUS_ADDER_EMPTY);
            }
            if (offsetRead > adderOffset) {
                return new ResultReplyRead(GlobalDefine.TF_STATUS_POINT_EQUALS);
            }
            if (offsetRead == GlobalDefine.TF_DEFAULT_HEADER_VALUE) {
                ATFLogger.sysinfo("TFReader中，追赶指针偏移量是-1,将偏移量设置为0，开始读取第一条");
                offsetRead = 0;
            }

            int code;
            byte[] data = null;
            //读取数据的长度
            ATFLogger.sysinfo("开始读取数据的长度");
            short len = getRecordLen();
            ATFLogger.sysinfo("读取数据长度：" + len);
            if (len == 0) {
                String msg = "数据的长度为空，adder : " + adderOffset + " read : " + offsetRead;
                ATFLogger.syserr(msg);
                initWithOffset(offsetRead, 0);
                return new ResultReplyRead(GlobalDefine.TF_STATUS_READ_REDO);
            }
            //读取数据的内容
            data = readRecData(offsetRead, len, buffBody.remaining());
            if (data == null || data.length == 0) {
                String msg = "数据的长度为空，adder : " + adderOffset + " read : " + offsetRead;
                ATFLogger.syserr(msg);
                initWithOffset(offsetRead, 0);
                return new ResultReplyRead(GlobalDefine.TF_STATUS_READ_REDO);
                //  throw new Exception(msg);
            }
            try {
                String type = BaseHeader.getTypeAndTradeType(data);
                if ("23".equals(type)) {
                    return new ResultReplyRead(GlobalDefine.TF_STATUS_END);
                }
            } catch (Exception e) {
                String msg = "数据的长度小于10不能获取数据类型，adder : " + adderOffset + " read : " + offsetRead;
                ATFLogger.syserr(msg);
                initWithOffset(offsetRead, 0);
                return new ResultReplyRead(GlobalDefine.TF_STATUS_READ_REDO);
            }
            ResultReplyRead replyRead = iReadProcess.doRead(this, offsetRead, data);
            buffReadAndWriterHead.write(offsetRead + data.length + GlobalDefine.TF_RECORD_LEN_BYTE);
            return replyRead;
        } finally {
            wtlock.unlock();
        }
    }

    private short getRecordLen() {
        short len;
        if (buffBody.remaining() == 1) {
            byte[] bb = new byte[2];
            bb[0] = buffBody.get();
            pager.setPageNo(pager.getPageNo() + 1);
            createBody(pager.getFirst(), pager.getPageSize());
            bb[1] = buffBody.get();
            len = (short) ByteUtil.readShort(bb, 0);
        } else if (buffBody.remaining() > 1) {
            len = buffBody.getShort();
        } else {
            pager.setPageNo(pager.getPageNo() + 1);
            createBody(pager.getFirst(), pager.getPageSize());
            len = buffBody.getShort();
        }
        return len;
    }

    private byte[] readRecData(int offsetRead, int len, int remain) {
        //如果内存里面的量足够读取本条记录，则直接读取
        if (remain >= len) {
            byte[] data = new byte[len];
            buffBody.get(data);
            return data;
        }
        //否则分2次读取
        else {
            byte[] b1 = new byte[remain];
            buffBody.get(b1);
            byte[] b2 = new byte[len - remain];
            pager.setPageNo(pager.getPageNo() + 1);
            createBody(pager.getFirst(), pager.getPageSize());
            buffBody.get(b2);
            byte[] result = new byte[len];
            System.arraycopy(b1, 0, result, 0, b1.length);
            System.arraycopy(b2, 0, result, b1.length, b2.length);
            return result;
        }
    }

    public void initBody() {
        createBody(1, pager.getPageSize());
    }

    private void createBody(int start, int pageSize) {
        try {
//      System.out.println("pager first : "+(pager.getFirst()-1));
            buffBody = channel.map(FileChannel.MapMode.READ_ONLY, GlobalDefine.TF_HEAD_LEN_ALL + start - 1, pageSize);
        } catch (IOException e) {
            e.printStackTrace();
            String msg = "TFReader中切换内存发生错误 " + e.toString();
            ATFLogger.syserr(msg);
            throw new TFException(ATFConstants.ATF_TF_MEM_CHANGE_ERR, msg);
        }

    }

    //根据交易流水读取TF文件中的记录
    public static byte[] readByFlowId(String path, String flowId) {
        try {
            String tfname = flowId.substring(0, 12);
            String dateOffset = flowId.substring(12);
            int lDateOffet = Integer.valueOf(dateOffset);
            RandomAccessFile destFile = new RandomAccessFile(path + tfname, "rw");
            FileChannel channel = destFile.getChannel();
            MappedByteBuffer lenBuffer = channel.map(FileChannel.MapMode.READ_ONLY, lDateOffet, GlobalDefine.TF_RECORD_LEN_BYTE);
            int dataLen = lenBuffer.getInt();
            MappedByteBuffer data = channel.map(FileChannel.MapMode.READ_ONLY, lDateOffet + GlobalDefine.TF_RECORD_LEN_BYTE, dataLen);
            byte[] result = new byte[dataLen];
            data.get(result);
            channel.close();
            destFile.close();
            return result;
        } catch (Exception e) {
            throw new RuntimeException("读取TF数据发生错误");
        }
    }


    /**
     * 关闭文件池
     */
    public boolean baseClose() {
        try {
            if (accfile != null) {
                accfile.close();
            }
            if (channel != null) {
                channel.close();
            }
            return true;
        } catch (Throwable th) {
            return false;
        }
    }

}

